﻿using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Runtime.InteropServices;
using Hyperion.Native;

namespace Hyperion.Injector
{
    /// <summary>
    /// An injected library!
    /// </summary>
    public class InjectedLibrary
    {
        public ProcessModule Module { get; private set; }
        public IntPtr BaseAddress { get { return Module.BaseAddress; } }
        public string LibraryPath { get; private set; }

        /// <summary>
        /// Initializes a new instance of the <see cref="InjectedLibrary" /> class.
        /// </summary>
        /// <param name="path">The path.</param>
        /// <param name="module">The module.</param>
        public InjectedLibrary(string path, ProcessModule module)
        {
            LibraryPath = path;
            Module = module;
        }

        /// <summary>
        /// Resolves an export of this specific library and returns a pointer relative to the module.
        /// </summary>
        /// <param name="exportName">Name of the export.</param>
        /// <returns></returns>
        /// <exception cref="Hyperion.MemoryException">Couldn't resolve the specified export: {0}!</exception>
        public IntPtr ResolveExport(string exportName)
        {
            IntPtr ret;
            var libPtr = IntPtr.Zero;

            try
            {
                // First off, we load the library of this module into our own process, so we can call GetProcAddress on it and make it relative later on.
                libPtr = Win32.LoadLibraryExW(Module.FileName, IntPtr.Zero, LoadLibraryExFlags.DontResolveDllReferences);
                if (libPtr == IntPtr.Zero)
                    throw new MemoryException(
                        "Failed to load the library into the local process using LoadLibraryExW! [Win32 Error: {0}]",
                        Marshal.GetLastWin32Error(), new Win32Exception(Marshal.GetLastWin32Error()));

                var funcPtr = Win32.GetProcAddress(libPtr, exportName);
                if (funcPtr == IntPtr.Zero)
                    throw new MemoryException("Couldn't find the specified export {0} in the loaded library!", exportName);

                // Rebase the func pointer to be relative to its base address, so we can CreateRemoteThread on [Module + relativeFuncPtr] later on.
                ret = new IntPtr(funcPtr.ToInt64() - (long) Module.BaseAddress);
            }
            finally
            {
                Win32.CloseHandle(libPtr);
            }

            return ret;
        }
    }
}
